package com.gkzf.ai.module.crm.service.creditReport;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gkzf.ai.framework.common.enums.UserTypeEnum;
import com.gkzf.ai.framework.common.pojo.CommonResult;
import com.gkzf.ai.framework.common.pojo.PageResult;
import com.gkzf.ai.framework.common.util.object.BeanUtils;
import com.gkzf.ai.framework.tenant.core.context.TenantContextHolder;
import com.gkzf.ai.framework.web.core.util.WebFrameworkUtils;
import com.gkzf.ai.framework.websocket.core.sender.local.LocalWebSocketMessageSender;
import com.gkzf.ai.module.crm.controller.admin.creditReport.vo.admittancecondition.CreditReportAdmittanceConditionSaveReqVO;
import com.gkzf.ai.module.crm.controller.admin.creditReport.vo.creditreportbasic.CreditReportBasicPageReqVO;
import com.gkzf.ai.module.crm.controller.admin.creditReport.vo.creditreportbasic.CreditReportBasicRespVO;
import com.gkzf.ai.module.crm.controller.admin.creditReport.vo.creditreportbasic.CreditReportBasicSaveReqVO;
import com.gkzf.ai.module.crm.dal.dataobject.creditReport.CreditReportBasicDO;
import com.gkzf.ai.module.crm.dal.dataobject.creditReport.CreditReportDO;
import com.gkzf.ai.module.crm.dal.dataobject.product.ProductMatchDO;
import com.gkzf.ai.module.crm.dal.mysql.creditReport.CreditReportBasicMapper;
import com.gkzf.ai.module.crm.dal.mysql.creditReport.CreditReportMapper;
import com.gkzf.ai.module.crm.service.product.admin.ProductMatchService;
import com.gkzf.ai.module.infra.api.file.FileApi;
import com.gkzf.ai.module.system.api.dept.DeptApi;
import com.gkzf.ai.module.system.api.dict.DictDataApi;
import com.gkzf.ai.module.system.api.dict.dto.DictDataRespDTO;
import com.gkzf.ai.module.system.api.user.AdminUserApi;
import com.gkzf.ai.module.system.api.user.dto.AdminUserRespDTO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;

import java.io.InputStream;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;

import static com.gkzf.ai.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.gkzf.ai.module.crm.enums.ErrorCodeConstants.CREDIT_REPORT_BASIC_NOT_EXISTS;
import static com.gkzf.ai.module.crm.util.IdNumberUtils.redactIdCard;
import static com.gkzf.ai.module.crm.util.QRCodeGenerator.*;

/**
 * CRM 征信报告基础 Service 实现类
 *
 * @author 国科智飞
 */
@Slf4j
@Service
@Validated
public class CreditReportBasicServiceImpl implements CreditReportBasicService {


    @Value("${creditReportQRCodeUrl}")
    private String creditReportUrl;
    @Resource
    private CreditReportBasicMapper creditReportBasicMapper;

    @Resource
    private CreditReportService creditReportService;

    @Resource
    private CreditReportMapper creditReportMapper;

    @Resource
    private DictDataApi dictDataApi;

    @Resource
    private DeptApi deptApi;

    @Resource
    private FileApi fileApi;

    @Resource
    private AdminUserApi adminUserApi;

    @Resource
    private CreditReportAdmittanceConditionService creditReportAdmittanceConditionService;

    @Resource
    private ProductMatchService productMatchService;


    @Resource
    private LocalWebSocketMessageSender localWebSocketMessageSender;

    @Value("${productMatchUrl}")
    private String productMatchUrl;

    @Override
    @Async
    public void createCreditReport(CreditReportBasicDO creditReportBasic,String channelType,Long currentUserId) {
        log.info("enter creditReportBasicServiceImpl");
        CreditReportDO creditReportDO = new CreditReportDO();
        // 插入 CreditReport 征信报告 并更新
        try {
            log.info("className 109:{}", this.getClass().getName());
            execAnalysis(creditReportBasic, creditReportDO,  channelType);
        } catch (Exception e) {
            updateCreditReportStatus(2, creditReportBasic);
            log.error("处理异常，请重试", e);
        }
        creditReportMapper.insert(creditReportDO);
        Map<String,String> resultMap = new HashMap<>();
        resultMap.put("update","1");
        log.info("推送解析结束消息： 用户类型:{}，用户ID:{}", UserTypeEnum.ADMIN.getValue(),currentUserId);
        localWebSocketMessageSender.send(UserTypeEnum.ADMIN.getValue(), currentUserId,"creditReport", JSON.toJSONString(resultMap));
    }

    /**
     * 执行解析方法
     * @param creditReportBasic 基本表对象里面有数据需要解析填
     * @param creditReportDO
     */
    public void execAnalysis(CreditReportBasicDO creditReportBasic, CreditReportDO creditReportDO,String channelType) throws Exception {
        CreditReportBasicDO creditReport = creditReportService.createCreditReport(creditReportBasic,creditReportDO,channelType);
        creditReport.setStatus(1);
        creditReportBasicMapper.updateById(creditReport);
    }

    /**
     * 修改状态解析状态
     * @param status 状态
     * @param creditReportBasic 基本表对象
     */
    public void updateCreditReportStatus(Integer status,CreditReportBasicDO creditReportBasic){
        creditReportBasic.setStatus(status);
        creditReportBasicMapper.updateById(creditReportBasic);
    }



    @Override
//    @LogRecord(type = CRM_CREDIT_REPORT_BASIC_TYPE, subType = CRM_CREDIT_REPORT_CREATE_SUB_TYPE, bizNo = "{{#creditReportBasicVo.id}}",
//            success = CRM_CREDIT_REPORT_CREATE_SUCCESS)
    public CreditReportBasicDO createCreditReportBasic(CreditReportBasicSaveReqVO creditReportBasicVo,Long currentUserId) {
        // 插入
        CreditReportBasicDO creditReportBasic = BeanUtils.toBean(creditReportBasicVo, CreditReportBasicDO.class);
//        creditReportBasic.setIdNumber(creditReportBasicVo.getIdNumber());
        creditReportBasic.setCreator(String.valueOf(currentUserId));
        creditReportBasic.setUpdater(String.valueOf(currentUserId));
        creditReportBasic.setStatus(0);
        creditReportBasicMapper.insert(creditReportBasic);
        DictDataRespDTO dictData = dictDataApi.getDictData("logo_config", TenantContextHolder.getTenantId() + "");
        InputStream inputStream = downloadImageFromUrl(dictData.getCssClass());
        byte[] bytes;
        if (null != inputStream){
            bytes = generateQRCodeImageWithLogo(creditReportUrl+"?creditBasicId=" + creditReportBasic.getId()+"&tenantId="+ TenantContextHolder.getTenantId()+"&userId="+WebFrameworkUtils.getLoginUserId(),  300, 300, inputStream,0.2f);
        }else {
            bytes = generateQRCodeImage(creditReportUrl+"?creditBasicId=" + creditReportBasic.getId()+"&tenantId="+ TenantContextHolder.getTenantId(),  300, 300);
        }
        String file = fileApi.createFile(bytes);
        creditReportBasic.setQrCode(file);
        creditReportBasicMapper.updateById(creditReportBasic);
        log.info("新增征信基本信息完成，获取基础对象:{}",JSON.toJSONString(creditReportBasic.toString()));
        return creditReportBasic;
    }

    /**
     * al匹配接口
     * @param id 匹配ID
     * @return 匹配描述
     */
    @Override
    public CommonResult<Object> matchProduct(Long id) {
        /*
         * 获取收入负债比和额度
         * 并更新
         */
        Map<String, String> incomeDebtRatio = calculationQuotaDebtRatio(id);
        // 请求匹配接口
        String result = HttpUtil.get(productMatchUrl+"&cid=" + id);
        Map<String,Object> resultMap;
        try {
             resultMap = JSON.parseObject(result, new TypeReference<>() {});
        }catch (Exception e){
            return CommonResult.success(false,"匹配失败");
        }
        List<ProductMatchDO> productMatch = new ArrayList<>();
        List<ProductMatchDO> productMatchRepeat = new ArrayList<>();
        // 匹配成功产品逻辑
        matchSuccess(id, resultMap, incomeDebtRatio, productMatch);
        // 匹配失败产品逻辑
        matchError(id, resultMap, productMatch, productMatchRepeat);
        // 修改基础表是否匹配字段状态
        CreditReportBasicDO creditReportBasic = new CreditReportBasicDO();
        creditReportBasic.setId(id);
        creditReportBasic.setIsMatch(1);
        creditReportBasicMapper.updateById(creditReportBasic);
        // 匹配表数据操作逻辑
        productMatchService.insertProductMatch(productMatch,id);
       return CommonResult.success("匹配成功");
    }

    /**
     * 匹配失败产品
     * @param id 征信基本表id
     * @param resultMap 匹配结果
     * @param productMatch 存储匹配失败描述不是收入负债比的 产品
     * @param productMatchRepeat 存储匹配匹配失败描述是收入负债比不通过的 产品
     */
    private void matchError(Long id, Map<String, Object> resultMap, List<ProductMatchDO> productMatch, List<ProductMatchDO> productMatchRepeat) {
        insertProductMatch(resultMap,"failedReasons", object ->{
            if(object instanceof JSONArray jsonArray){
                Iterator<Object> iterator = jsonArray.stream().iterator();
                while (iterator.hasNext()){
                    Object next = iterator.next();
                    if (next instanceof JSONObject jsonObject){
                        /*
                         * 封装为对象
                         */
                        String productNo = jsonObject.getString("productNo");
                        String userDetail = jsonObject.getString("userDetail");
                        String productRequirement = jsonObject.getString("productRequirement");
                        String reason = jsonObject.getString("reason");
                        ProductMatchDO productMatchDO = new ProductMatchDO();
                        productMatchDO.setProductNo(productNo);
                        productMatchDO.setStatus(1);
                        productMatchDO.setCreditLimit("0");
                        productMatchDO.setUserCondition(userDetail);
                        productMatchDO.setProductCondition(productRequirement);
                        productMatchDO.setErrorResult(reason);
                        productMatchDO.setCreditBasicId(id);
                        boolean isProductNo = productMatch.stream().anyMatch(item -> item.getProductNo().equals(jsonObject.getString("productNo")));
                        /*
                            收入负债比描述单独判断
                         */
                          /*
                            收入负债比描述单独判断
                         */
                        if (reason.contains("收入负债比不匹配")){
                            productMatchRepeat.add(productMatchDO);
                        } else if(isProductNo){
                            // 用来判断重复的集合 叠加
                            productMatch.stream().filter(item -> item.getProductNo().equals(productNo)).forEach(item -> {
                                item.setUserCondition(item.getUserCondition()+"_"+userDetail);
                                item.setProductCondition(item.getProductCondition()+"_"+productRequirement);
                                item.setErrorResult(item.getErrorResult()+"_"+reason);
                            });
                        }else {
                            productMatch.add(productMatchDO);
                        }
                    }
                }
            }

            /*
             * 收入负债比单独判断
             * 匹配结果去除重复数据
             */
            List<String> idsInB = productMatch.stream()
                    .map(ProductMatchDO::getProductNo)
                    .toList();

            List<ProductMatchDO> diffById = productMatchRepeat.stream()
                    .filter(u -> !idsInB.contains(u.getProductNo()))
                    .toList();
            /*
                当匹配失败描述 就一个收入负债比时添加一个默认数据，否则在下面的循环中会直接忽略这组数据
             */
            diffById.forEach(item -> {
                boolean anyMatch = productMatch.stream().anyMatch(it -> it.getProductNo().equals(item.getProductNo()));
                if (!anyMatch) {
                    ProductMatchDO productMatchDO = new ProductMatchDO();
                    productMatchDO.setProductNo(item.getProductNo());
                    productMatchDO.setStatus(1);
                    productMatchDO.setCreditLimit("0");
                    productMatchDO.setUserCondition("");
                    productMatchDO.setProductCondition("");
                    productMatchDO.setErrorResult("");
                    productMatchDO.setCreditBasicId(id);
                    productMatch.add(productMatchDO);
                }
            });

            /*
             *  如果一个产品有多个收入负债比不通过
             *  则优化收集前置条件不符合的描述
             */
            productMatch.forEach(item -> {
                List<ProductMatchDO> list = productMatchRepeat.stream().filter(it -> it.getProductNo().equals(item.getProductNo())).toList();
                if (list.isEmpty()) {
                    return;
                }
                if (list.size() == 1) {
                    item.setUserCondition(convert().apply(item.getUserCondition()) + list.get(0).getUserCondition());
                    item.setProductCondition(convert().apply(item.getProductCondition()) + list.get(0).getProductCondition());
                    item.setErrorResult(convert().apply(item.getErrorResult()) + list.get(0).getErrorResult());
                    return;
                }
                ProductMatchDO productMatchDO = list.stream().filter(ll -> !ll.getErrorResult().contains("前置条件不符合)")).findFirst().orElse(null);
                if (productMatchDO != null) {
                    item.setUserCondition(convert().apply(productMatchDO.getUserCondition()) + productMatchDO.getUserCondition());
                    item.setProductCondition(convert().apply(item.getProductCondition()) + productMatchDO.getProductCondition());
                    item.setErrorResult(convert().apply(item.getErrorResult()) + productMatchDO.getErrorResult());
                } else {
                    item.setUserCondition(convert().apply(item.getUserCondition()) + list.get(0).getUserCondition());
                    item.setProductCondition(convert().apply(item.getProductCondition()) + list.get(0).getProductCondition());
                    item.setErrorResult(convert().apply(item.getErrorResult()) + list.get(0).getErrorResult());
                }

            });
        });
    }

    /**
     * 匹配成功产品
     * @param id 征信基础信息
     * @param resultMap 结果
     * @param incomeDebtRatio 产品编号-额度 结果 Map
     * @param productMatch  匹配结果表集合
     */
    private void matchSuccess(Long id, Map<String, Object> resultMap, Map<String, String> incomeDebtRatio, List<ProductMatchDO> productMatch) {
        insertProductMatch(resultMap,"passedProducts", object ->{
            if(object instanceof JSONObject jsonObject){
                for (Map.Entry<String, Object> next : jsonObject.entrySet()) {
                    ProductMatchDO productMatchDO = new ProductMatchDO();
                    productMatchDO.setProductNo(next.getKey());
                    productMatchDO.setStatus(0);
                    productMatchDO.setCreditBasicId(id);
                    if (incomeDebtRatio.containsKey(next.getKey())) {
                        productMatchDO.setCreditLimit(incomeDebtRatio.get(next.getKey()));
                    }
                    productMatch.add(productMatchDO);
                }
            }
        });
    }

    /**
     * 计算收入负债比和额度计算
     * @param id 详版基本信息ID
     * @return 返回额度结果和收入负债比结果
     */
    private Map<String, String> calculationQuotaDebtRatio(Long id) {
        Map<String,String> incomeDebtRatio = creditReportAdmittanceConditionService.getIncomeDebtRatio(id);
        CreditReportAdmittanceConditionSaveReqVO updateReqVO = new CreditReportAdmittanceConditionSaveReqVO();
        if (StringUtils.isNotBlank(incomeDebtRatio.get("incomeDebtRatio"))) {
        updateReqVO.setCreditBasicId(id);
        updateReqVO.setIncomeDebtRatio(incomeDebtRatio.get("incomeDebtRatio"));
        creditReportAdmittanceConditionService.updateCreditReportAdmittanceConditionXml(updateReqVO);
        }
        return incomeDebtRatio;
    }

    public void insertProductMatch(Map<String,Object> resultMap,String key, Consumer<Object> comsumer){
        if (resultMap.containsKey(key)){
            Object failedReasons = resultMap.get(key);
            comsumer.accept(failedReasons);

        }
    }


    public static Function<String, String> convert() {
        // 1. 定义一个 BiFunction 来计算百分比率
        return  numerator -> {
            if (StringUtils.isNotBlank(numerator)){
                return numerator+"_";
            }else {
                return "";
            }
        };
    }


    @Override
    public void updateCreditReportBasic(CreditReportBasicSaveReqVO updateReqVO) {
        // 校验存在
        validateCreditReportBasicExists(updateReqVO.getId());
        // 更新
        updateReqVO.setIdNumber(updateReqVO.getIdNumber());

        CreditReportBasicDO updateObj = BeanUtils.toBean(updateReqVO, CreditReportBasicDO.class);
        creditReportBasicMapper.updateById(updateObj);
    }

    @Override
    public void deleteCreditReportBasic(Long id) {
        // 校验存在
        validateCreditReportBasicExists(id);
        // 删除
        creditReportBasicMapper.deleteById(id);
    }

    private void validateCreditReportBasicExists(Long id) {
        if (creditReportBasicMapper.selectById(id) == null) {
            throw exception(CREDIT_REPORT_BASIC_NOT_EXISTS);
        }
    }

    @Override
    public CreditReportBasicDO getCreditReportBasic(Long id) {
        CreditReportBasicDO creditReportBasicDO = creditReportBasicMapper.selectById(id);
        creditReportBasicDO.setIdNumber(creditReportBasicDO.getIdNumber());
        return creditReportBasicDO;
    }

    @Override
    public PageResult<CreditReportBasicRespVO> getCreditReportBasicPage(CreditReportBasicPageReqVO pageReqVO) {
        List<Long> deptUser = deptApi.getDeptUser(WebFrameworkUtils.getLoginUserId());
        List<AdminUserRespDTO> userListByDeptIds = adminUserApi.getUserListByDeptIds(deptUser);
        List<Long> userList = new ArrayList<>(userListByDeptIds.stream().map(AdminUserRespDTO::getId).toList());
        userList.add(WebFrameworkUtils.getLoginUserId());
        pageReqVO.setUserList(userList);
        IPage<CreditReportBasicDO> page = creditReportBasicMapper.selectCreditReportBasicPage(new Page<>(pageReqVO.getPageNo(),pageReqVO.getPageSize()), pageReqVO);
        // 循环数据
        List<CreditReportBasicRespVO> respVOList = BeanUtils.toBean(page.getRecords(), CreditReportBasicRespVO.class);
        // getList() 为空
        if (CollectionUtils.isEmpty(page.getRecords())){
            return new PageResult<>(new ArrayList<>(),page.getTotal());
        }

        for (CreditReportBasicRespVO creditReportBasic : respVOList) {
            BigDecimal loanObjective = creditReportAdmittanceConditionService.getLoanObjective(creditReportBasic.getId());
            AdminUserRespDTO user = adminUserApi.getUser(Long.parseLong(creditReportBasic.getCreator()));
            // 2.2 设置创建人、负责人名称
            if (Objects.nonNull(user)){
                creditReportBasic.setCreateName(user.getNickname());
            }
            if (Objects.nonNull(creditReportBasic.getIdNumber())){
                creditReportBasic.setIdNumber(creditReportBasic.getIdNumber());
            }
            if (creditReportBasic.getStatus() == 1) {
                creditReportBasic.setException(redactIdCard(creditReportBasic.getIdNumber()));
            }
            creditReportBasic.setLoanObjective(loanObjective);
        }
        return new PageResult<>(respVOList,page.getTotal());
    }

    public void updateIsSubmitQpCode(Long id, Integer educational){
        String education;
        switch (educational) {
            case 1 -> education = "高中及以下";
            case 2 -> education = "大专";
            case 3 -> education = "本科";
            case 4 -> education = "硕士";
            case 5 -> education = "博士";
            default -> education = "";
        }
        CreditReportBasicDO crmProductDO = new CreditReportBasicDO();
        crmProductDO.setIsSubmitQpCode(1);
        crmProductDO.setEducation(education);
        crmProductDO.setId(id);
        creditReportBasicMapper.updateById(crmProductDO);
    }

}